﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P3SS.Networking;
using System.Xml.Serialization;
using P3SS.Security;

namespace P3SS.Engine
{
	/// <summary>
	/// Provides the base implementation for an engine
	/// </summary>
	public abstract class AbstractEngine
	{
		#region Fields
		/// <summary>
		/// The protocol that this engine uses
		/// </summary>
		protected AbstractProtocol protocol;
		/// <summary>
		/// Does the engine need to stop?
		/// </summary>
		protected bool cancellationPending;
		#endregion
		#region Properties
		/// <summary>
		/// The hostname of the server to connect to
		/// </summary>
		public string Server
		{
			get
			{ return protocol.Server; }
			set
			{ protocol.Server = value; }
		}

		/// <summary>
		/// The port number of the server to connect to
		/// </summary>
		public int Port
		{
			get
			{ return protocol.Port; }
			set
			{ protocol.Port = value; }
		}

		/// <summary>
		/// Type of encryption to use when connecting
		/// </summary>
		public EncryptionType Encryption
		{
			get
			{ return protocol.Encryption; }
			set
			{ protocol.Encryption = value; }
		}

		/// <summary>
		/// Username to use when authenticating
		/// </summary>
		public string Username
		{
			get
			{ return protocol.Username; }
			set
			{ protocol.Username = value; }
		}

		/// <summary>
		/// Password to use when authenticating (in plaintext)
		/// </summary>
		[XmlIgnore]
		public string Password
		{
			get
			{ return protocol.Password; }
			set
			{ protocol.Password = value; }
		}

		/// <summary>
		/// Password to use when authenticating (encrypted for storage)
		/// </summary>
		public string SecurePassword
		{
			get
			{ return EncryptionHandler.EncryptString(protocol.Password, protocol.Username); }
			set
			{ protocol.Password = EncryptionHandler.DecryptString(value, protocol.Username); }
		}
		#endregion
		#region Protected Methods
		/// <summary>
		/// Passes a message up from the engine
		/// </summary>
		/// <param name="message">Message to pass</param>
		/// <param name="debugInfo">Additional debugging info to pass</param>
		/// <param name="typeOfMessage">Type of message that is being passed</param>
		protected void PassMessage(string message, string debugInfo, MessageType typeOfMessage)
		{
			RaiseMessageOccurredEvent(message, typeOfMessage);
			if (!string.IsNullOrEmpty(debugInfo))
			{
				RaiseMessageOccurredEvent(debugInfo, MessageType.Debug);
			}
		}

		/// <summary>
		/// Passes a debug message up from the engine
		/// </summary>
		/// <param name="message">Debug message to pass</param>
		protected void PassDebugMessage(string message)
		{
			RaiseMessageOccurredEvent(message, MessageType.Debug);
		}
		#endregion
		#region Public Methods
		/// <summary>
		/// Stops the engine
		/// </summary>
		public void StopEngine()
		{
			cancellationPending = true;
		}
		/// <summary>
		/// Checks that all settings are valid
		/// </summary>
		/// <returns>True if all settings are valid, otherwise false</returns>
		public virtual bool ValidateSettings()
		{
			return protocol.ValidateSettings();
		}

		/// <summary>
		/// Human readable string representing this object
		/// </summary>
		/// <returns>Human readable string specifying the server and port</returns>
		public override string ToString()
		{
			string outString = Server + ":" + Port;

			switch (Encryption)
			{
				case EncryptionType.Implicit:
					outString += "-";
					break;
				case EncryptionType.Explicit:
					outString += "+";
					break;
			}

			return outString;
		}
		#endregion
		#region Events
		/// <summary>
		/// Occurs when the engine wished to pass an async message
		/// </summary>
		public event System.EventHandler<EngineMessageEventArgs> MessageOccurred;
		/// <summary>
		/// Causes a MessageOccured event to be invoked
		/// Use PassMessage or PassDebugMessage instead
		/// </summary>
		/// <param name="message">Message to pass from the engine</param>
		/// <param name="typeOfMessage">Type of message that is being passed</param>
		private void RaiseMessageOccurredEvent(string message, MessageType typeOfMessage)
		{
			if (MessageOccurred != null)
			{
				MessageOccurred.Invoke(this, new EngineMessageEventArgs() { Message = message, TypeOfMessage = typeOfMessage });
			}
		}
		#endregion
	}
}
